/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.binding;

import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 * @author Lasse Voss
 */

/**
 * Mapper 注册器用于将所有 mapper 接口添加到内存中， Mapper 注册器自身维护着两个属性，config 和 knownMappers,
 * knownMappers 是一个 Map 集合。表示某个类路径对应的 Mapper 代理工厂。
 *
 * MapperRegistry 用于注册、获取和判断是否 Mapper接口已经被注册的功能，
 * 1.getMapper(Class type, SqlSession sqlSession) 从缓存集合中获取对应的Mapper接口
 * 2.hasMapper(Class type) 判断缓存集合中是否存在Mapper接口
 * 3.addMapper(Class type) 添加Mapper接口到knownMappers集合中
 * 4.getMappers() 获取knownMappers集合中所有已经注册的Mapper接口，Mybatis3.2.2新增
 * 5.addMappers(String packageName, Class<?> superType) 添加某个包下的所有符合superType的子类或者子接口
 */
public class MapperRegistry {

  /**
   * 全局配置
   */
  private final Configuration config;
  /**
   * MapperProxyFactory做的事比较简单,
   * 目的就是通过代理模式创建处一个MapperProxy,MapperProxy实现了InvocationHandler接口,
   * 这表示MapperProxy会通过invoke()方法实现Mapper接口指定方法的调用,
   * MapperProxy并不直接实现Mapper接口的调用,而是在内部维系着一个<Mapper.Method,MapperMethod>的map集合
   */
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  /**
   * 该方法用于获取已经注册的 Mapper动态代理对象,
   * @param type
   * @param sqlSession
   * @param <T>
   * @return
   */
  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  
  public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
  }

  /**
   * 根据 mapper 接口 Class 对象 创建 MapperProxyFactory对象，并注册到 knownMappers属性中
   * @param type
   * @param <T>
   */
  public <T> void addMapper(Class<T> type) {
    // 对于mybatis mapper 接口文件，必须是 interface,不能是class
    if (type.isInterface()) {
      // 判重  判断是否已经注册过
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        // knownMappers 是MapperRegistry 的主要字段，维护了 Mapper 接口和代理类的映射关系
        // key 是 mapper 接口，value 是MapperProxyFactory
        // 将 Mapper 接口注册到 map 集合中
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        // 创建 MapperAnnotationBuilder 进行具体解析
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        // 还需要完成一次 xml解析，如果解析不成功  在 finaly 中还是会将 Mapper 接口移除。
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

  /**
   * @since 3.2.2
   */
  public Collection<Class<?>> getMappers() {
    return Collections.unmodifiableCollection(knownMappers.keySet());
  }

  /**
   * @since 3.2.2
   */
  public void addMappers(String packageName, Class<?> superType) {
    // ResolverUtil 用于查找在类路径可用并满足任意条件的类。
    //mybatis框架提供的搜索classpath下指定package以及子package中符合条件(注解或者继承于某个类/接口)的类，
    // 默认使用Thread.currentThread().getContextClassLoader()返回的加载器,和spring的工具类殊途同归。
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    // 查找 packageName 包下的所有 Object 的子类，也就是所有类
    // 通过find 方法找到匹配的类之后会 存放在 ResolverUtil.matches 中
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    // 所有匹配的 class 都被存储在 ResolverUtil.matches字段中
    // resolverUtil.getClasses()  ->  return matches
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    // 遍历这些匹配的类
    for (Class<?> mapperClass : mapperSet) {
      // 调用 addMapper 方法进行具体的 mapper类/接口解析
      addMapper(mapperClass);
    }
  }

  /** 外部调用的路口
   * packageName : 包名
   * @since 3.2.2
   */
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }
  
}
